#include <iostream>
#include <fstream>
#include <string>
#include <vector>

#include "data_store.h"

using namespace std;

Data_store::Data_store()
{
  _section_item = new Section_item("Main");
}

Data_store::~Data_store()
{
  if (_section_item != NULL) {
    delete _section_item;
  }
}

void Data_store::load()
{
  ifstream file;
  file.open((_name + string(".store")).c_str());
  if (!file.is_open()) {
    cout << "ERROR: Unable to open file " << _name + string(".store") << endl;
    exit(1);
  }
  load(file);
  file.close();
}

void Data_store::load(string name)
{
  _name = name;
  load();
}

void Data_store::load(istream &file)
{
  string line;
  vector<string> in_section;
  vector<Item *> section_items;
  //  delete _section_item;
  //  Item *section_item = NULL;
  Item *section_item = _section_item;
  while (!file.eof()) {
    getline(file, line);
    vector<string> tokens = tokenize(line, string(" "));
    //    cout << line << " " << tokens.size() << endl;

    if (tokens[0][0] == '/' || tokens.size() < 4) { // skip empty lines and comments
      continue;
    }

    if (tokens[0][0] == '[' && tokens.size() > 2) {
      if (tokens[2] == "Main")
        continue;
      if (tokens[1] == "BEGIN") {
        in_section.push_back(tokens[2]);
        //        cout << "Found section: " << tokens[2] << endl;
        Item *new_section_item = new Section_item(tokens[2]);
        if (section_item != NULL) {
          section_item->add_item(new_section_item);
          section_items.push_back(section_item);
        }
        else {
          _section_item = new_section_item;
        }
        section_item = new_section_item;
        continue;
      }
      if (tokens[1] == "END") {
        string section = in_section[in_section.size() - 1];
        in_section.pop_back();
        if (tokens[2] != section) {
          // todo: throw exception
          cout << "ERROR: Found section end " << tokens[2] << ", expected "
              << section << endl;
        }
        section_item = section_items[section_items.size() - 1];
        section_items.pop_back();
        continue;
      }
    }
    create_item_from_tokens(section_item, tokens);
  }
}

void Data_store::create_item_from_tokens(Item *section_item,
    vector<string> &tokens)
{
  Item *new_item;
  switch (atoi(tokens[0].c_str()))
  {
  case doubles:
    new_item = new Double_item(tokens[1]);
    for (int i = 3; i < (int) tokens.size(); ++i) {
      new_item->add_double(atof(tokens[i].c_str()));
    }
    section_item->add_item(new_item);
    //    cout << "doubles: " << tokens[1] << endl;
    break;
  case integers:
    new_item = new Integer_item(tokens[1]);
    for (int i = 3; i < (int) tokens.size(); ++i) {
      new_item->add_integer(atoi(tokens[i].c_str()));
    }
    section_item->add_item(new_item);
    break;
  case strings:
    new_item = new String_item(tokens[1]);
    for (int i = 3; i < (int) tokens.size(); ++i) {
      new_item->add_string(tokens[i]);
    }
    section_item->add_item(new_item);
    break;
  default:
    cout << "ERROR: Unknown item type: " << tokens[0] << endl;
  }
}

void Data_store::save()
{
  ofstream file;
  file.open((_name + string(".store")).c_str());
  _section_item->save(file);
  file.close();
}

void Data_store::save(ostream &os)
{
  _section_item->save(os);
}

Item *Data_store::get_item()
{
  return _section_item;
}

Item *Data_store::get_item(string item)
{
  return _section_item->get_item(item);
}

void Data_store::set_name(string name)
{
  _name = name;
}

vector<string> Data_store::tokenize(const string &str, const string &delim)
{
  using namespace std;
  vector<string> tokens;

  size_t p0 = 0, p1 = string::npos;
  while (p0 != string::npos) {
    p1 = str.find_first_of(delim, p0);
    if (p1 != p0) {
      string token = str.substr(p0, p1 - p0);
      tokens.push_back(token);
    }
    p0 = str.find_first_not_of(delim, p1);
  }

  // combine tokens inside ""
  vector<string> finaltokens;
  for (unsigned int x = 0; x < tokens.size(); x++) {
    // Regular argument to add
    if ((int) tokens[x].find("\"") == -1)
      finaltokens.push_back(tokens[x]);
    else {
      // Save it as the first string
      string construct = tokens[x];
      // see if we need to go on
      if ((int) tokens[x].rfind("\"") == 0) {
        // Go to the next element
        x++;
        // Loop while we have elements
        while (x < tokens.size()) {
          // Add it in
          construct += (" " + tokens[x]);
          if ((int) tokens[x].find("\"") != -1)
            break; // We're done!
          // Next element
          x++;
        }
      }

      // Erase the two quotes
      construct.erase(construct.find("\""), 1);
      construct.erase(construct.find("\""), 1);

      // Save this argument
      finaltokens.push_back(construct);
    }
  }

  return finaltokens;
}
